﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using MediaAssistant.Constants;
using MediaAssistant.DAL;
using MediaAssistant.Helper;

namespace MediaAssistant.Management
{
    [Export]
    public class MediaFileScanner : BackgroundScanner
    {
        private List<string> ExistingFiles { get; set; }

        private Queue<string> ScanFolderQueue { get; set; }

        public Dictionary<string, FileSystemWatcher> FileSystemWatchers { get; set; }
        public MediaFileScanner()
        {
            ExistingFiles=new List<string>();
            Name = "Media File Scanner";
            ScanFolderQueue=new Queue<string>();
            FileSystemWatchers=new Dictionary<string, FileSystemWatcher>();
        }
        public override void Stop()
        {
            base.Stop();
            IsScanStarted = false;
            foreach (var fileSystemWatcher in FileSystemWatchers)
            {
                UnsubscribeEvents(fileSystemWatcher.Value);
            }
            FileSystemWatchers.Clear();
        }

        private void UnsubscribeEvents(FileSystemWatcher fileSystemWatcher)
        {
            fileSystemWatcher.Created -= HandleFileCreated;
            fileSystemWatcher.EnableRaisingEvents = false;
            fileSystemWatcher.Dispose();
        }

        public bool IsScanStarted { get; set; }
        protected override void TryToScan()
        {
            if(IsScanStarted)
                return;
            IsScanStarted = true;
            switch (RegistryHelper.ScanOption)
            {
                case ScanOption.SpecificFolder:
                    QueueSpecificFolders();
                    break;
                case ScanOption.MyComputer:
                    foreach (var driveInfo in DriveInfo.GetDrives().Where(driveInfo => driveInfo.IsReady))
                    {
                        Enqueue(driveInfo.RootDirectory.FullName);
                    }
                    break;
                case ScanOption.None:
                    return;
                case ScanOption.MyDocument:
                    Enqueue(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
                    Enqueue(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic));
                    Enqueue(Environment.GetFolderPath(Environment.SpecialFolder.MyVideos));
                    break;
            }
            GetExistingFiles();
            ScanQueuedFolder();
            SetStatusMessage(StatusMessages.Ready);
        }

        private void Enqueue(string fullName)
        {
            Watch(fullName);
            ScanFolderQueue.Enqueue(fullName);
        }

        private void Watch(string fullName)
        {
            if (FileSystemWatchers.ContainsKey(fullName))
                return;
            var watcher = new FileSystemWatcher(fullName);
            SubscribeEvents(watcher);
            //watcher.NotifyFilter = NotifyFilters.FileName;
            watcher.IncludeSubdirectories = true;
            watcher.EnableRaisingEvents = true;
            FileSystemWatchers.Add(fullName,watcher);
        }

        private void SubscribeEvents(FileSystemWatcher watcher)
        {
            watcher.Created += HandleFileCreated;
        }

        private void HandleFileCreated(object sender, FileSystemEventArgs e)
        {
            if (e.ChangeType != WatcherChangeTypes.Created) 
                return;
            
            if(File.Exists(e.FullPath))
                AddFile(e.FullPath);
            else if(Directory.Exists(e.FullPath))
            {
                Scan(e.FullPath);
            }
        }

        private void QueueSpecificFolders()
        {
            foreach (var scannedDirectory in DatabaseManager.Instance.GetScanDirectories())
            {
                if (scannedDirectory.KeepEye == false && scannedDirectory.LastScanned != null)
                {
                    continue;
                }
                if (!Directory.Exists(scannedDirectory.FullPath))
                {
                    continue;
                }
                Enqueue(scannedDirectory.FullPath);
            }  
        }


        private void ScanQueuedFolder()
        {
            while(ScanFolderQueue.Count>0)
            {
                var folderPath = ScanFolderQueue.Dequeue();
                if (!Directory.Exists(folderPath))
                {
                    continue;
                }
                if (Scan(folderPath))
                {
                    var scannedDirectory = DatabaseManager.Instance.GetScanDirectories().FirstOrDefault(d => d.FullPath == folderPath);
                    if(scannedDirectory!=null)
                        DatabaseManager.Instance.MarkAsScanned(scannedDirectory);
                }
                if (ShallReturn())
                    return;
                PauseIfRequested();
            }
        }

        private void GetExistingFiles()
        {
            ExistingFiles=new List<string>();
            ExistingFiles.AddRange(DatabaseManager.Instance.GetImportedFileNames().Where(f => ExistingFiles.Contains(f) == false));
        }

        private bool Scan(string folderPath)
        {
            try
            {
                if (Directory.Exists(folderPath) == false)
                    return false;
                if (ShallReturn())
                    return false;
                PauseIfRequested();
                AddFiles(Directory.GetFiles(folderPath, "*.mp3"));
                if (ShallReturn())
                    return false;
                PauseIfRequested();
                AddFiles(Directory.GetFiles(folderPath, "*.wma"));
                foreach (var extension in Utility.SupportedMovieFileExtension)
                {
                    if (ShallReturn())
                        return false;
                    PauseIfRequested();
                    AddFiles(Directory.GetFiles(folderPath, "*" + extension));
                }
                foreach (var directory in Directory.GetDirectories(folderPath))
                {
                    if (Scan(directory) == false)
                        return false;
                    if (ShallReturn())
                        return false;
                    PauseIfRequested();
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
            
        }

        private void AddFiles(IEnumerable<string> files)
        {
            foreach (var file in files)
            {
                AddFile(file);
                if (ShallReturn())
                    return;
                PauseIfRequested();
            }
        }

        private void AddFile(string fileName)
        {
            if (ExistingFiles.Contains(fileName))
                return;
            ExistingFiles.Add(fileName);
            if (Utility.IsMusicFile(fileName))
            {
                if(DatabaseManager.Instance.IsMusicFileAdded(fileName)==false)
                {
                    SetStatusMessage(string.Format("Adding music file {0}", fileName));
                    DatabaseManager.Instance.AddMusicFile(fileName);
                }
            }
            else if (Utility.IsMovieFile(fileName))
            {
                var fileInfo = new FileInfo(fileName);
                if (fileInfo.Length < Utility.MinMovieFileSize)
                    return;
                if (DatabaseManager.Instance.IsMovieFileAdded(fileName) == false)
                {
                    SetStatusMessage(string.Format("Adding movie file {0}", fileName));
                    DatabaseManager.Instance.AddMovieFile(fileName);
                    DatabaseManager.Instance.UpdateNewMovieTitle(DataSource.Value.ShowDrivesMoviesOnly, DataSource.Value.SelectedProfile);
                    DatabaseManager.Instance.UpdateProcessingTitle(DataSource.Value.ShowDrivesMoviesOnly);
                }
            }
        }
    }
}
